using System;
using System.Collections.Generic;
using System.Text;
using EnvDTE;
using Microsoft.VisualStudio.Modeling;
using EnvDTE80;
using DslFactory.Utilities.DteCodeModel;
using System.Net.Security;
using System.ServiceModel;

namespace Dslfactory.wcfLanguage
{
 public partial class Service
 {
  /// <summary>
  /// Show the concepts that are used by this concept
  /// </summary>
  public override void ShowUsedConcepts()
  {
   KnownCodeTypes knownCodeTypes = KnownCodeTypes.FromStore(Store);
   if (knownCodeTypes == null)
    return;

   CodeClass2 codeClass = knownCodeTypes.GetFullNamedType(FullName) as CodeClass2;
   if (codeClass == null)
    return;

   foreach(CodeInterface iface in codeClass.ImplementedInterfaces)
    if (InterfaceReverseEngineering.CanMerge(iface as CodeType))
    {
     ServiceContract serviceContract = InterfaceReverseEngineering.MergeType(ServicesDefinition, iface as CodeType) as ServiceContract;
     if (serviceContract != null)
      if (!ImplementedServiceContracts.Contains(serviceContract))
       ImplementedServiceContracts.Add(serviceContract);
    }
  }
 }

 /// <summary>
 /// Update the DataContract of the model from the code
 /// </summary>
 /// <param name="servicesDefinition">Root element of the model</param>
 /// <param name="knownCodeTypes">Known types</param>
 internal class ServiceReverseEngineering
 {
  /// <summary>
  /// Update the Interfaces and ServiceContract of the model from the code
  /// </summary>
  /// <param name="servicesDefinition">Root element of the model</param>
  /// <param name="knownCodeTypes">Known types</param>
  public static void UpdateServices(ServicesDefinition servicesDefinition, KnownCodeTypes knownCodeTypes)
  {
   List<Service> services = new List<Service>(servicesDefinition.Services); // Important (collection might be modified)
   foreach (Service s in services)
   {
    CodeType codeClass = (knownCodeTypes.GetFullNamedType(s.FullName) as CodeClass) as CodeType;
    if (codeClass != null)
     MergeService(servicesDefinition, codeClass);
   }
  }
  public static bool CanMerge(CodeType codeType)
  {
   if (codeType is CodeClass2)
    return (IsService(codeType as CodeClass2));
   else
    return false;
  }


  internal static Service MergeType(ServicesDefinition ServicesDefinition, CodeType codeType)
  {
   if (codeType is CodeClass2)
    if (IsService(codeType as CodeClass2))
     return MergeService(ServicesDefinition, codeType);
   return null;
  }



  public static bool IsService(CodeClass2 codeClass)
  {
   if (IncrementalGenerator.HasAttribute(codeClass as CodeType, serviceBehaviorAttributeFullTypeName))
    return true;

   foreach (CodeInterface codeInterface in codeClass.ImplementedInterfaces)
   {
    if (IncrementalGenerator.HasAttribute(codeInterface as CodeType, serviceBehaviorAttributeFullTypeName))
     return true;
   }
   return false;
  }

  private static Service MergeService(ServicesDefinition ServicesDefinition, CodeType codeType)
  {
   Store Store = ServicesDefinition.Store;
   Service service = ServicesDefinition.FindServiceOfFullName(codeType.FullName);
    using (Transaction transaction = ServicesDefinition.Store.TransactionManager.BeginTransaction("Updating Service"))
    {
     if (service == null)
     {
      // Add service
      service = new Service(ServicesDefinition.Store);
      service.FullName = codeType.FullName;
      ServicesDefinition.Services.Add(service);
     }

     // Add relationships
     foreach (CodeInterface codeInterface in (codeType as CodeClass2).ImplementedInterfaces)
      if (IncrementalGenerator.HasAttribute(codeInterface as CodeType, serviceBehaviorAttributeFullTypeName))
      {
       ServiceContract serviceContract = ServicesDefinition.FindServiceContractOfFullName(codeInterface.FullName);
       if (   (serviceContract != null)
           && (service.ImplementedServiceContracts.IndexOf(serviceContract) == -1)
          )
        service.ImplementedServiceContracts.Add(serviceContract);
      }

     // Update service properties from attribute
     UpdateServiceProperties(codeType, service);
     if (transaction.HasPendingChanges)
      transaction.Commit();
    }
    return service;
  }


  static readonly string serviceBehaviorAttributeFullTypeName = "System.ServiceModel.ServiceBehaviorAttribute";
  /// <summary>
  /// Update the service properties
  /// </summary>
  /// <param name="codeType">Type implementing the service</param>
  /// <param name="service">Service to update</param>
  private static void UpdateServiceProperties(CodeType codeType, Service service)
  {
   // Message body
   CodeAttribute2 attribute = IncrementalGenerator.FindFirstAttribute(codeType, serviceBehaviorAttributeFullTypeName);
   if (attribute != null)
   {
    IncrementalGenerator.UpdateDomainPropertiesFromAttributeArguments(service,
                                                                      attribute,
                                                                      new List<string>(new string[] 
                                                                             {
                                                                              "AddressFilterMode",
                                                                              "AutomaticSessionShutdown",
                                                                              "ConcurrencyMode",
                                                                              "ConfigurationName",
                                                                              "IgnoreExtensionDataObject",
                                                                              "IncludeExceptionDetailInFaults",
                                                                              "InstanceContextMode",
                                                                              "MaxItemsInObjectGraph",
                                                                              "Name->WSDLName",
                                                                              "Namespace->WSDLNamespace",
                                                                              "ReleaseServiceInstanceOnTransactionComplete",
                                                                              "TransactionAutoCompleteOnSessionClose",
                                                                              "TransactionIsolationLevel",
                                                                              "TransactionTimeout",
                                                                              "UseSynchronizationContext",
                                                                              "ValidateMustUnderstand"
                                                                             })
                                                            );
   }
  }

  /// <summary>
  /// Can the reverse engineering service process the attribute changement
  /// </summary>
  internal static bool CanProcessAttributeAddedOrChanged(CodeAttribute2 attribute)
  {
   try { return attribute.FullName == "System.ServiceModel.ServiceBehaviorAttribute"; }
   catch (Exception) { return attribute.Name == "ServiceBehavior"; }
  }


  internal static void ProcessAttributeAddedOrChanged(ServicesDefinition servicesDefinition, CodeAttribute2 attribute)
  {
   // Data Contact
   if (CanProcessAttributeAddedOrChanged(attribute))
   {
    CodeType codeClass = attribute.Parent as CodeType;
    if (codeClass != null)
    {
     Service service = servicesDefinition.FindServiceOfFullName(codeClass.FullName);
     if (service == null)
      return;

     using (Transaction transaction = servicesDefinition.Store.TransactionManager.BeginTransaction(string.Format("Reversing attribute '{0}' on class '{1}'", attribute.Name, codeClass.FullName)))
     {
      UpdateServiceProperties(codeClass, service);
      if (transaction.HasPendingChanges)
       transaction.Commit();
     }
    }
   }
  }
 }
}
